]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st-theme-node: Support non-uniform border widths
authorFlorian Müllner <fmuellner@gnome.org>
Tue, 23 Nov 2010 14:10:57 +0000 (15:10 +0100)
committerFlorian Müllner <fmuellner@gnome.org>
Mon, 13 Dec 2010 17:15:00 +0000 (18:15 +0100)
While non-uniform border widths were parsed correctly, an arbitrary
side's width was picked when painting, so that each border ended up
with the same width and the widths specified in CSS were ignored.

At least for sides between non-rounded corners, using a different
border width can be reasonable, for instance at screen edges.

Different border widths around rounded corners are kind of crack,
but then it would be lame not to support it ...

https://bugzilla.gnome.org/show_bug.cgi?id=607500

src/st/st-theme-node-drawing.c
tests/Makefile.am
tests/interactive/border-width.js [new file with mode: 0644]

index 4a17ac2da548994d200e77d828926fdffb634b8f..6e71c9881573015f5b4c978cf43afda42c66cc43 100644 (file)
@@ -49,6 +49,22 @@ typedef struct {
   guint          border_width_2;
 } StCornerSpec;
 
+static void
+elliptical_arc (cairo_t *cr,
+                double   x_center,
+                double   y_center,
+                double   x_radius,
+                double   y_radius,
+                double   angle1,
+                double   angle2)
+{
+  cairo_save (cr);
+  cairo_translate (cr, x_center, y_center);
+  cairo_scale (cr, x_radius, y_radius);
+  cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
+  cairo_restore (cr);
+}
+
 static CoglHandle
 create_corner_material (StCornerSpec *corner)
 {
@@ -73,13 +89,11 @@ create_corner_material (StCornerSpec *corner)
   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
   cairo_scale (cr, size, size);
 
-  /* TODO support nonuniform border widths */
-
-  if (corner->border_width_1 < corner->radius)
+  if (max_border_width <= corner->radius)
     {
-      double internal_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+      double x_radius, y_radius;
 
-      if (corner->border_width_1 != 0)
+      if (max_border_width != 0)
         {
           cairo_set_source_rgba (cr,
                                  corner->border_color_1.red / 255.,
@@ -96,14 +110,41 @@ create_corner_material (StCornerSpec *corner)
                              corner->color.green / 255.,
                              corner->color.blue / 255.,
                              corner->color.alpha / 255.);
-      cairo_arc (cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+
+      x_radius = 0.5 * (1.0 - (double) corner->border_width_2 / corner->radius);
+      y_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+
+      /* TOPRIGHT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      3 * M_PI / 2, 2 * M_PI);
+
+      /* BOTTOMRIGHT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      0, M_PI / 2);
+
+      /* TOPLEFT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      M_PI, 3 * M_PI / 2);
+
+      /* BOTTOMLEFT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      M_PI / 2, M_PI);
+
       cairo_fill (cr);
     }
   else
     {
       double radius;
 
-      radius = (gdouble)corner->radius / corner->border_width_1;
+      radius = (gdouble)corner->radius / max_border_width;
 
       cairo_set_source_rgba (cr,
                              corner->border_color_1.red / 255.,
@@ -216,6 +257,41 @@ over (const ClutterColor *source,
   unpremultiply (result);
 }
 
+static void
+st_theme_node_get_corner_border_widths (StThemeNode *node,
+                                        StCorner     corner_id,
+                                        guint       *border_width_1,
+                                        guint       *border_width_2)
+{
+  switch (corner_id)
+    {
+      case ST_CORNER_TOPLEFT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_TOP];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_LEFT];
+        break;
+      case ST_CORNER_TOPRIGHT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_TOP];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        break;
+      case ST_CORNER_BOTTOMRIGHT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        break;
+      case ST_CORNER_BOTTOMLEFT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_LEFT];
+        break;
+    }
+}
+
 static CoglHandle
 st_theme_node_lookup_corner (StThemeNode    *node,
                              StCorner        corner_id)
@@ -233,30 +309,25 @@ st_theme_node_lookup_corner (StThemeNode    *node,
 
   corner.radius = node->border_radius[corner_id];
   corner.color = node->background_color;
+  st_theme_node_get_corner_border_widths (node, corner_id,
+                                          &corner.border_width_1,
+                                          &corner.border_width_2);
 
   switch (corner_id)
     {
       case ST_CORNER_TOPLEFT:
-        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
-        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_TOPRIGHT:
-        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
-        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_BOTTOMRIGHT:
-        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
-        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_BOTTOMLEFT:
-        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
-        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
         break;
@@ -346,28 +417,14 @@ get_background_position (StThemeNode             *self,
 }
 
 /* Use of this function marks code which doesn't support
- * non-uniform widths and/or colors.
+ * non-uniform colors.
  */
-static gboolean
-get_arbitrary_border (StThemeNode   *node,
-                      int           *width,
-                      ClutterColor  *color)
+static void
+get_arbitrary_border_color (StThemeNode   *node,
+                            ClutterColor  *color)
 {
-  int w;
-
-  w = st_theme_node_get_border_width (node, ST_SIDE_TOP);
-  if (w > 0)
-    {
-      if (width)
-        *width = w;
-      if (color)
-        st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
-      return TRUE;
-    }
-
-  if (width)
-    *width = 0;
-  return FALSE;
+  if (color)
+    st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
 }
 
 static CoglHandle
@@ -379,7 +436,7 @@ st_theme_node_render_gradient (StThemeNode *node)
   cairo_surface_t *surface;
   cairo_pattern_t *pattern;
   ClutterColor border_color;
-  int border_width;
+  int border_width[4];
   guint rowstride;
   guchar *data;
 
@@ -392,11 +449,15 @@ st_theme_node_render_gradient (StThemeNode *node)
                                                  rowstride);
   cr = cairo_create (surface);
 
-  /* TODO - support non-uniform border colors and widths */
-  get_arbitrary_border (node, &border_width, &border_color);
+  /* TODO - support non-uniform border colors */
+  get_arbitrary_border_color (node, &border_color);
 
   for (i = 0; i < 4; i++)
-    radius[i] = st_theme_node_get_border_radius (node, i);
+    {
+      border_width[i] = st_theme_node_get_border_width (node, i);
+
+      radius[i] = st_theme_node_get_border_radius (node, i);
+    }
 
   if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
     pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
@@ -437,13 +498,13 @@ st_theme_node_render_gradient (StThemeNode *node)
                radius[ST_CORNER_TOPRIGHT],
                radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
   cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]);
-  if (radius[ST_CORNER_BOTTOMRIGHT])
+  if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
     cairo_arc (cr,
                node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
                node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
                radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
   cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height);
-  if (radius[ST_CORNER_BOTTOMLEFT])
+  if (radius[ST_CORNER_BOTTOMLEFT] > 0)
     cairo_arc (cr,
                radius[ST_CORNER_BOTTOMLEFT],
                node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
@@ -456,7 +517,10 @@ st_theme_node_render_gradient (StThemeNode *node)
    * otherwise the outline shape is filled with the background
    * gradient directly
    */
-  if (border_width > 0)
+  if (border_width[ST_SIDE_TOP] > 0 ||
+      border_width[ST_SIDE_RIGHT] > 0 ||
+      border_width[ST_SIDE_BOTTOM] > 0 ||
+      border_width[ST_SIDE_LEFT] > 0)
     {
       cairo_set_source_rgba (cr,
                              border_color.red / 255.,
@@ -465,47 +529,70 @@ st_theme_node_render_gradient (StThemeNode *node)
                              border_color.alpha / 255.);
       cairo_fill (cr);
 
-      if (radius[ST_CORNER_TOPLEFT] > border_width)
-        cairo_arc (cr,
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT] - border_width,
-                   M_PI, 3 * M_PI / 2);
+      if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
+                                          border_width[ST_SIDE_LEFT]))
+        elliptical_arc (cr,
+                        radius[ST_CORNER_TOPLEFT],
+                        radius[ST_CORNER_TOPLEFT],
+                        radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
+                        radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
+                        M_PI, 3 * M_PI / 2);
       else
-        cairo_move_to (cr, border_width, border_width);
+        cairo_move_to (cr,
+                       border_width[ST_SIDE_LEFT],
+                       border_width[ST_SIDE_TOP]);
 
       cairo_line_to (cr,
-                     node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width),
-                     border_width);
-
-      if (radius[ST_CORNER_TOPRIGHT] > border_width)
-        cairo_arc (cr,
-                   node->alloc_width - radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT] - border_width,
-                   3 * M_PI / 2, 2 * M_PI);
+                     node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
+                     border_width[ST_SIDE_TOP]);
+
+      if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
+                                           border_width[ST_SIDE_RIGHT]))
+        elliptical_arc (cr,
+                        node->alloc_width - radius[ST_CORNER_TOPRIGHT],
+                        radius[ST_CORNER_TOPRIGHT],
+                        radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
+                        radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
+                        3 * M_PI / 2, 2 * M_PI);
+      else
+        cairo_line_to (cr,
+                       node->alloc_width - border_width[ST_SIDE_RIGHT],
+                       border_width[ST_SIDE_TOP]);
 
       cairo_line_to (cr,
-                     node->alloc_width - border_width,
-                     node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width));
-
-      if (radius[ST_CORNER_BOTTOMRIGHT] > border_width)
-        cairo_arc (cr,
-                   node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
-                   node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
-                   radius[ST_CORNER_BOTTOMRIGHT] - border_width,
-                   0, M_PI / 2);
+                     node->alloc_width - border_width[ST_SIDE_RIGHT],
+                     node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
+
+      if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
+                                              border_width[ST_SIDE_RIGHT]))
+        elliptical_arc (cr,
+                        node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
+                        node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
+                        radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
+                        radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
+                        0, M_PI / 2);
+      else
+        cairo_line_to (cr,
+                       node->alloc_width - border_width[ST_SIDE_RIGHT],
+                       node->alloc_height - border_width[ST_SIDE_BOTTOM]);
 
       cairo_line_to (cr,
-                     MAX(radius[ST_CORNER_BOTTOMLEFT], border_width),
-                     node->alloc_height - border_width);
-
-      if (radius[ST_CORNER_BOTTOMLEFT] > border_width)
-        cairo_arc (cr,
-                   radius[ST_CORNER_BOTTOMLEFT],
-                   node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
-                   radius[ST_CORNER_BOTTOMLEFT] - border_width,
-                   M_PI / 2, M_PI);
+                     MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
+                     node->alloc_height - border_width[ST_SIDE_BOTTOM]);
+
+      if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
+                                             border_width[ST_SIDE_LEFT]))
+        elliptical_arc (cr,
+                        radius[ST_CORNER_BOTTOMLEFT],
+                        node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
+                        radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
+                        radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
+                        M_PI / 2, M_PI);
+      else
+        cairo_line_to (cr,
+                       border_width[ST_SIDE_LEFT],
+                       node->alloc_height - border_width[ST_SIDE_BOTTOM]);
+
       cairo_close_path (cr);
     }
 
@@ -706,27 +793,40 @@ st_theme_node_paint_borders (StThemeNode           *node,
 
 {
   float width, height;
-  int border_width;
+  int border_width[4];
   int max_border_radius = 0;
   int max_width_radius[4];
-  int corner_id;
+  int corner_id, side_id;
   ClutterColor border_color;
   guint8 alpha;
 
   width = box->x2 - box->x1;
   height = box->y2 - box->y1;
 
-  get_arbitrary_border (node, &border_width, &border_color);
+  /* TODO - support non-uniform border colors */
+  get_arbitrary_border_color (node, &border_color);
+
+  for (side_id = 0; side_id < 4; side_id++)
+    border_width[side_id] = st_theme_node_get_border_width(node, side_id);
+
   for (corner_id = 0; corner_id < 4; corner_id++)
     {
+      guint border_width_1, border_width_2;
+
+      st_theme_node_get_corner_border_widths (node, corner_id,
+                                              &border_width_1, &border_width_2);
+
       if (node->border_radius[corner_id] > max_border_radius)
         max_border_radius = node->border_radius[corner_id];
-      max_width_radius[corner_id] = MAX(border_width,
+      max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2),
                                         node->border_radius[corner_id]);
     }
 
   /* borders */
-  if (border_width > 0)
+  if (border_width[ST_SIDE_TOP] > 0 ||
+      border_width[ST_SIDE_RIGHT] > 0 ||
+      border_width[ST_SIDE_BOTTOM] > 0 ||
+      border_width[ST_SIDE_LEFT] > 0)
     {
       ClutterColor effective_border;
       gboolean skip_corner_1, skip_corner_2;
@@ -749,18 +849,19 @@ st_theme_node_paint_borders (StThemeNode           *node,
           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0;
           y1 = 0;
           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width;
-          y2 = border_width;
+          y2 = border_width[ST_SIDE_TOP];
           cogl_rectangle (x1, y1, x2, y2);
 
           /* EAST */
           skip_corner_1 = node->border_radius[ST_CORNER_TOPRIGHT] > 0;
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
 
-          x1 = width - border_width;
-          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT] : border_width;
+          x1 = width - border_width[ST_SIDE_RIGHT];
+          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT]
+                             : border_width[ST_SIDE_TOP];
           x2 = width;
           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT]
-                             : height - border_width;
+                             : height - border_width[ST_SIDE_BOTTOM];
           cogl_rectangle (x1, y1, x2, y2);
 
           /* SOUTH */
@@ -768,7 +869,7 @@ st_theme_node_paint_borders (StThemeNode           *node,
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
 
           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0;
-          y1 = height - border_width;
+          y1 = height - border_width[ST_SIDE_BOTTOM];
           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT]
                              : width;
           y2 = height;
@@ -779,10 +880,11 @@ st_theme_node_paint_borders (StThemeNode           *node,
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0;
 
           x1 = 0;
-          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : border_width;
-          x2 = border_width;
+          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT]
+                             : border_width[ST_SIDE_TOP];
+          x2 = border_width[ST_SIDE_LEFT];
           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT]
-                             : height - border_width;
+                             : height - border_width[ST_SIDE_BOTTOM];
           cogl_rectangle (x1, y1, x2, y2);
         }
     }
@@ -853,82 +955,99 @@ st_theme_node_paint_borders (StThemeNode           *node,
           switch (corner_id)
             {
               case ST_CORNER_TOPLEFT:
-                verts[0] = border_width;
-                verts[1] = max_width_radius[ST_CORNER_TOPLEFT];
+                verts[0] = border_width[ST_SIDE_LEFT];
+                verts[1] = MAX(node->border_radius[corner_id],
+                               border_width[ST_SIDE_TOP]);
                 verts[2] = max_border_radius;
                 verts[3] = max_border_radius;
                 if (n_rects == 2)
                   {
-                    verts[4] = max_width_radius[ST_CORNER_TOPLEFT];
-                    verts[5] = border_width;
+                    verts[4] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_LEFT]);
+                    verts[5] = border_width[ST_SIDE_TOP];
                     verts[6] = max_border_radius;
-                    verts[7] = max_width_radius[ST_CORNER_TOPLEFT];
+                    verts[7] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_TOP]);
                   }
                 break;
               case ST_CORNER_TOPRIGHT:
                 verts[0] = width - max_border_radius;
-                verts[1] = max_width_radius[ST_CORNER_TOPRIGHT];
-                verts[2] = width - border_width;
+                verts[1] = MAX(node->border_radius[corner_id],
+                               border_width[ST_SIDE_TOP]);
+                verts[2] = width - border_width[ST_SIDE_RIGHT];
                 verts[3] = max_border_radius;
                 if (n_rects == 2)
                   {
                     verts[4] = width - max_border_radius;
-                    verts[5] = border_width;
-                    verts[6] = width - max_width_radius[ST_CORNER_TOPRIGHT];
-                    verts[7] = max_width_radius[ST_CORNER_TOPRIGHT];
+                    verts[5] = border_width[ST_SIDE_TOP];
+                    verts[6] = width - MAX(node->border_radius[corner_id],
+                                           border_width[ST_SIDE_RIGHT]);
+                    verts[7] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_TOP]);
                   }
                 break;
               case ST_CORNER_BOTTOMRIGHT:
                 verts[0] = width - max_border_radius;
                 verts[1] = height - max_border_radius;
-                verts[2] = width - border_width;
-                verts[3] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT];
+                verts[2] = width - border_width[ST_SIDE_RIGHT];
+                verts[3] = height - MAX(node->border_radius[corner_id],
+                                        border_width[ST_SIDE_BOTTOM]);
                 if (n_rects == 2)
                   {
                     verts[4] = width - max_border_radius;
-                    verts[5] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT];
-                    verts[6] = width - max_width_radius[ST_CORNER_BOTTOMRIGHT];
-                    verts[7] = height - border_width;
+                    verts[5] = height - MAX(node->border_radius[corner_id],
+                                            border_width[ST_SIDE_BOTTOM]);
+                    verts[6] = width - MAX(node->border_radius[corner_id],
+                                           border_width[ST_SIDE_RIGHT]);
+                    verts[7] = height - border_width[ST_SIDE_BOTTOM];
                   }
                 break;
               case ST_CORNER_BOTTOMLEFT:
-                verts[0] = border_width;
+                verts[0] = border_width[ST_SIDE_LEFT];
                 verts[1] = height - max_border_radius;
                 verts[2] = max_border_radius;
-                verts[3] = height - max_width_radius[ST_CORNER_BOTTOMLEFT];
+                verts[3] = height - MAX(node->border_radius[corner_id],
+                                        border_width[ST_SIDE_BOTTOM]);
                 if (n_rects == 2)
                   {
-                    verts[4] = max_width_radius[ST_CORNER_BOTTOMLEFT];
-                    verts[5] = height - max_width_radius[ST_CORNER_BOTTOMLEFT];
+                    verts[4] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_LEFT]);
+                    verts[5] = height - MAX(node->border_radius[corner_id],
+                                            border_width[ST_SIDE_BOTTOM]);
                     verts[6] = max_border_radius;
-                    verts[7] = height - border_width;
+                    verts[7] = height - border_width[ST_SIDE_BOTTOM];
                   }
                 break;
             }
           cogl_rectangles (verts, n_rects);
         }
 
-      if (max_border_radius > border_width)
-        {
-          /* Once we've drawn the borders and corners, if the corners are bigger
-           * the the border width, the remaining area is shaped like
-           *
-           *  ########
-           * ##########
-           * ##########
-           *  ########
-           *
-           * We draw it in 3 pieces - first the top and bottom, then the main
-           * rectangle
-           */
-          cogl_rectangle (max_border_radius, border_width,
-                          width - max_border_radius, max_border_radius);
-          cogl_rectangle (max_border_radius, height - max_border_radius,
-                          width - max_border_radius, height - border_width);
-        }
-
-      cogl_rectangle (border_width, MAX(border_width, max_border_radius),
-                      width - border_width, height - MAX(border_width, max_border_radius));
+      /* Once we've drawn the borders and corners, if the corners are bigger
+       * then the border width, the remaining area is shaped like
+       *
+       *  ########
+       * ##########
+       * ##########
+       *  ########
+       *
+       * We draw it in at most 3 pieces - first the top and bottom if
+       * necessary, then the main rectangle
+       */
+      if (max_border_radius > border_width[ST_SIDE_TOP])
+        cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+                        border_width[ST_SIDE_TOP],
+                        width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+                        max_border_radius);
+      if (max_border_radius > border_width[ST_SIDE_BOTTOM])
+        cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+                        height - max_border_radius,
+                        width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+                        height - border_width[ST_SIDE_BOTTOM]);
+
+      cogl_rectangle (border_width[ST_SIDE_LEFT],
+                      MAX(border_width[ST_SIDE_TOP], max_border_radius),
+                      width - border_width[ST_SIDE_RIGHT],
+                      height - MAX(border_width[ST_SIDE_BOTTOM], max_border_radius));
     }
 }
 
@@ -1108,8 +1227,6 @@ st_theme_node_paint (StThemeNode           *node,
    *   zero width or a border image is being used.
    *
    * Deviations from the above as implemented here:
-   *  - Nonuniform border widths combined with a non-zero border radius result
-   *    in the border radius being ignored
    *  - The combination of border image and a non-zero border radius is
    *    not supported; the background color will be drawn with square
    *    corners.
index 4c6b92188fece0410a6b5f355e644731d555c8ad..9fb38a29495df1b11168c83fb0f840b0096cb4c7 100644 (file)
@@ -4,6 +4,7 @@ EXTRA_DIST = run-test.sh.in
 TEST_JS =                                      \
        interactive/borders.js                  \
        interactive/border-radius.js            \
+       interactive/border-width.js             \
        interactive/box-layout.js               \
        interactive/calendar.js                 \
        interactive/css-fonts.js                \
diff --git a/tests/interactive/border-width.js b/tests/interactive/border-width.js
new file mode 100644 (file)
index 0000000..60f7606
--- /dev/null
@@ -0,0 +1,60 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+
+const UI = imports.testcommon.ui;
+
+UI.init();
+let stage = Clutter.Stage.get_default();
+stage.width = 640;
+stage.height = 480;
+
+let vbox = new St.BoxLayout({ width: stage.width,
+                              height: stage.height,
+                              style: 'padding: 10px; background: #ffee88;'
+                            });
+stage.add_actor(vbox);
+
+let scroll = new St.ScrollView();
+vbox.add(scroll, { expand: true });
+
+let box = new St.BoxLayout({ vertical: true,
+                             style: 'spacing: 20px;' });
+scroll.add_actor(box);
+
+function addTestCase(borders, useGradient) {
+    let background;
+    if (useGradient)
+        background = 'background-gradient-direction: vertical;'
+                     + 'background-gradient-start: white;'
+                     + 'background-gradient-end: gray;';
+    else
+        background = 'background: white;';
+
+    let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
+                       "border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
+                       "border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
+                       "border-left: " + borders[St.Side.LEFT] + " solid black;";
+    box.add(new St.Label({ text: border_style,
+                           style: border_style
+                                  + 'border-radius: 0px 5px 15px 25px;'
+                                  + 'padding: 5px;' + background }),
+                         { x_fill: false });
+}
+
+// uniform backgrounds
+addTestCase([" 0px", " 5px", "10px", "15px"], false);
+addTestCase([" 5px", "10px", "15px", " 0px"], false);
+addTestCase(["10px", "15px", " 0px", " 5px"], false);
+addTestCase(["15px", " 0px", " 5px", "10px"], false);
+
+// gradient backgrounds
+addTestCase([" 0px", " 5px", "10px", "15px"], true);
+addTestCase([" 5px", "10px", "15px", " 0px"], true);
+addTestCase(["10px", "15px", " 0px", " 5px"], true);
+addTestCase(["15px", " 0px", " 5px", "10px"], true);
+
+stage.show();
+Clutter.main();
+stage.destroy();